﻿using AngleSharp.Dom;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Linq.Expressions;
using System.Reflection.Metadata.Ecma335;
using WAU.DAL;
using WAU.DTOs.CommodityModule;
using WAU.Models;
using WAU.Models.CommodityModels;
using WAU.Models.Framework;
using WAU.Utilities.Redis;
using WAU.Utils;
using MCacheExt = Microsoft.Extensions.Caching.Distributed.MDistributedCacheExtension;

namespace WAU.Services
{
    /// <summary>
    /// 商品相关服务
    /// </summary>
    public class CommodityService
    {
        private readonly EssentialDbContext dbContext;

        private readonly BaseRepoMysql<Commodity> repoCommodity;
        private readonly BaseRepoMysql<CommoditySlideShowImage> repoCommodityImage;
        private readonly BaseRepoMysql<CommoditySKU> repoCommoditySKU;
        private readonly BaseRepoMysql<CommodityCategory> repoCommodityCategory;

        private readonly ILogger<CommodityService> logger;

        private readonly RepoMySql repoMysql = new RepoMySql();

        private const string COMMODITY_DETAIL_CACHE_TAG = "CommodityDetail";

        private readonly IDistributedCache _cache;


        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="dbContext"></param>
        /// <param name="logger"></param>
        public CommodityService(
            IServiceProvider serviceProvider,
            EssentialDbContext dbContext,
            ILogger<CommodityService> logger,
            IDistributedCache distributedCache)
        {
            this.dbContext = dbContext;

            repoCommodity = new BaseRepoMysql<Commodity>(dbContext);
            repoCommodityImage = new BaseRepoMysql<CommoditySlideShowImage>(dbContext);
            repoCommoditySKU = new BaseRepoMysql<CommoditySKU>(dbContext);
            repoCommodityCategory = new BaseRepoMysql<CommodityCategory>(dbContext);

            this.logger = logger;

            _cache = distributedCache;
        }

        /// <summary>
        /// 获取商品列表（管理端）
        /// </summary>
        /// <param name="lcDto"></param>
        /// <returns></returns>
        public async Task<QueryResult<CommodityListItemSco>> GetCommodityList(ListCommodityDto lcDto)
        {
            var commodityQuery = repoCommodity.Query(a => true);

            if (!string.IsNullOrEmpty(lcDto.CommodityName))
            {
                commodityQuery = commodityQuery.Where(a => a.CommodityName.Contains(lcDto.CommodityName));
            }

            var finalQuery = from commodity in commodityQuery
                             join skuCount in 
                                from sku in repoCommoditySKU.Query(a => true)
                                group sku by sku.CommodityId into temp1
                                select new
                                {
                                    CommodityId = temp1.Key,
                                    SKUCount = temp1.Count()
                                } on commodity.Id equals skuCount.CommodityId into skuCountTemp
                             from skuCount in skuCountTemp.DefaultIfEmpty()
                             select new CommodityListItemSco
                             {
                                 Id = commodity.Id,
                                 CommodityName = commodity.CommodityName,
                                 MainImageUrl = commodity.MainImageUrl,
                                 Manufacture = commodity.Manufacture,
                                 CreateTime = commodity.CreateTime,
                                 LastModifyTime = commodity.ModifyTime,
                                 SKUCount = (int?)skuCount.SKUCount ?? 0
                             };

            if (!string.IsNullOrEmpty(lcDto.SortField))
            {
                var ordering = CommonUtils.MakeOrderExpression<CommodityListItemSco>(lcDto.SortField);
                return await repoMysql.QueryResultByPageAsync(finalQuery, ordering, lcDto, a => a.Id, true);
            }
            else
            {
                finalQuery = finalQuery.OrderByDescending(a => a.LastModifyTime).ThenBy(a => a.Id);
                return await repoMysql.QueryResultByPageAsync(finalQuery, lcDto);
            }
        }

        /// <summary>
        /// 添加商品
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ExecResult> AddCommodity(string currentUserId, AddCommodityDto dto)
        {
            #region 添加商品主信息

            var duplicateNameQuery = repoCommodity.QueryNoTracking(a => a.CommodityName == dto.MainItem.CommodityName);
            if (duplicateNameQuery.Any())
            {
                return new ExecResult { Success = false, ErrorMsg = "已经存在相同名称的商品！" };
            }

            Commodity commodity = new Commodity();
            commodity.Id = GuidFactory.NewSortedGuid();
            commodity.CategoryId = dto.MainItem.CategoryId;
            commodity.CommodityName = dto.MainItem.CommodityName;
            commodity.Intro = dto.MainItem.Intro;
            commodity.Manufacture = dto.MainItem.Manufacture;
            commodity.MainImageUrl = dto.MainItem.MainImageUrl;

            commodity.Creator = currentUserId;
            commodity.CreateTime = DateTime.Now;

            repoCommodity.Add(commodity);

            #endregion

            #region 添加商品轮播图

            foreach (var image in dto.ImageDtoItems)
            {
                CommoditySlideShowImage commodityImage = new CommoditySlideShowImage();
                commodityImage.Id = GuidFactory.NewSortedGuid();
                commodityImage.CommodityId = commodity.Id;
                commodityImage.ImageUrl = image.ImageUrl;
                commodityImage.SortNum = image.SortNum;

                commodityImage.Creator = currentUserId;
                commodityImage.CreateTime = DateTime.Now;

                repoCommodityImage.Add(commodityImage);
            }

            #endregion

            #region 添加商品 SKU

            foreach (var sku in dto.SKUDtoItems)
            {
                CommoditySKU commoditySKU = new CommoditySKU();
                commoditySKU.Id = GuidFactory.NewSortedGuid();
                commoditySKU.CommodityId = commodity.Id;
                commoditySKU.SKUName = sku.SKUName;
                commoditySKU.Quantity = sku.Quantity;
                commoditySKU.Price = sku.Price;
                commoditySKU.SKUImageUrl = sku.SKUImageUrl;
                commoditySKU.SortNum = sku.SortNum;

                commoditySKU.Creator = currentUserId;
                commoditySKU.CreateTime = DateTime.Now;

                repoCommoditySKU.Add(commoditySKU);
            }

            #endregion

            await dbContext.SaveChangesAsync();
            return new ExecResult { Success = true, ErrorMsg = "添加商品成功！" };
        }

        /// <summary>
        /// 获取商品详情（原始版）
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ExecResult<CommodityDetailSco>> GetCommodityDetail(string id)
        {
            string cacheKey = $"{COMMODITY_DETAIL_CACHE_TAG}:{id}";
            bool cacheResult = _cache.TryGetFromDistributedCache(cacheKey, out CommodityDetailSco scoInCache);
            if (cacheResult)
            {
                return ExecResult<CommodityDetailSco>.RetSuccess(scoInCache, "获取商品详情成功");
            }

            CommodityDetailSco sco = new CommodityDetailSco();
            Commodity commodityEntity = await repoCommodity.FindAsyncNoTracking(a => a.Id == id);
            if (commodityEntity == null)
            {
                return ExecResult<CommodityDetailSco>.Error("未找到该商品");
            }

            List<CommoditySlideShowImage> commodityImageEntities = await repoCommodityImage.ListAsyncNoTracking(a => a.CommodityId == id);
            List<CommoditySKU> commoditySKUEntities = await repoCommoditySKU.ListAsyncNoTracking(a => a.CommodityId == id);

            sco.Main = new Commodity()
            {
                Id = commodityEntity.Id,
                CategoryId = commodityEntity.CategoryId,
                CommodityName = commodityEntity.CommodityName,
                Intro = commodityEntity.Intro,
                MainImageUrl = commodityEntity.MainImageUrl,
                Manufacture = commodityEntity.Manufacture
            };

            sco.SlideShowImageList = commodityImageEntities.OrderBy(a => a.SortNum)
                                .Select(a => new CommoditySlideShowImage
                                {
                                    Id = a.Id,
                                    ImageUrl = a.ImageUrl,
                                    SortNum = a.SortNum
                                }).ToList();

            sco.SKUList = commoditySKUEntities.OrderBy(a => a.SortNum)
                            .Select(a => new CommoditySKU
                            {
                                Id = a.Id,
                                CommodityId = a.CommodityId,
                                SKUName = a.SKUName,
                                Quantity = a.Quantity,
                                Price = a.Price,
                                SKUImageUrl = a.SKUImageUrl,
                                SortNum = a.SortNum
                            }).ToList();

            _cache.SetToDistributedCache(cacheKey, sco);
            return ExecResult<CommodityDetailSco>.RetSuccess(sco, "获取商品详情成功");
        }


        /// <summary>
        /// 获取商品详情（互斥锁应对缓存击穿）
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ExecResult<CommodityDetailSco>> GetCommodityDetailMutex(string id)
        {
            string cacheKey = $"{COMMODITY_DETAIL_CACHE_TAG}:{id}";
            bool cacheResult = _cache.TryGetFromDistributedCache(cacheKey, out CommodityDetailSco scoInCache);
            if (cacheResult)
            {
                if (scoInCache != null)
                {
                    return ExecResult<CommodityDetailSco>.RetSuccess(scoInCache, "获取商品详情成功");
                }
                else
                {
                    return ExecResult<CommodityDetailSco>.Error("未找到该商品");
                }
            }

            bool isGetLock = RedisClientService.TryGetDistributedLock(cacheKey);
            CommodityDetailSco sco = new CommodityDetailSco();
            try
            {
                if (!isGetLock)
                {
                    Console.WriteLine($"获取锁失败 - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
                    int count = 5;
                    while (count > 0)
                    {
                        count--;
                        Thread.Sleep(500);
                        bool isGetFromCache = _cache.TryGetFromDistributedCache(cacheKey, out CommodityDetailSco scoInCache1);
                        if (isGetFromCache)
                        {
                            if (scoInCache1 != null)
                            {
                                return ExecResult<CommodityDetailSco>.RetSuccess(scoInCache1, "获取商品详情成功");
                            }
                            else
                            {
                                return ExecResult<CommodityDetailSco>.Error("未找到该商品");
                            }
                        }
                    }

                    return ExecResult<CommodityDetailSco>.Error("未找到该商品");
                }
                // 获取锁了以后也再次判断下 DoubleCheck（因为主要目标是实现防止缓存击穿，与代码中的业务有一些差别）
                else
                {
                    Console.WriteLine($"获取锁成功 - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
                    bool isGetFromCache = _cache.TryGetFromDistributedCache(cacheKey, out CommodityDetailSco scoInCache1);
                    if (isGetFromCache)
                    {
                        if (scoInCache1 != null)
                        {
                            return ExecResult<CommodityDetailSco>.RetSuccess(scoInCache1, "获取商品详情成功");
                        }
                        else
                        {
                            return ExecResult<CommodityDetailSco>.Error("未找到该商品");
                        }
                    }
                }

                Console.WriteLine($"查询了数据库 - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
                Commodity commodityEntity = await repoCommodity.FindAsyncNoTracking(a => a.Id == id);
                if (commodityEntity == null)
                {
                    // 向缓存中设置空对象
                    _cache.SetToDistributedCache(cacheKey, MCacheExt.NULL_CACHE_VALUE, MCacheExt.NULL_CACHE_TTL_MINUTES);
                    return ExecResult<CommodityDetailSco>.Error("未找到该商品");
                }

                List<CommoditySlideShowImage> commodityImageEntities = await repoCommodityImage.ListAsyncNoTracking(a => a.CommodityId == id);
                List<CommoditySKU> commoditySKUEntities = await repoCommoditySKU.ListAsyncNoTracking(a => a.CommodityId == id);

                sco.Main = new Commodity()
                {
                    Id = commodityEntity.Id,
                    CommodityName = commodityEntity.CommodityName,
                    Intro = commodityEntity.Intro,
                    MainImageUrl = commodityEntity.MainImageUrl,
                    Manufacture = commodityEntity.Manufacture
                };

                sco.SlideShowImageList = commodityImageEntities.OrderBy(a => a.SortNum)
                                    .Select(a => new CommoditySlideShowImage
                                    {
                                        Id = a.Id,
                                        ImageUrl = a.ImageUrl,
                                        SortNum = a.SortNum
                                    }).ToList();

                sco.SKUList = commoditySKUEntities.OrderBy(a => a.SortNum)
                                .Select(a => new CommoditySKU
                                {
                                    Id = a.Id,
                                    CommodityId = a.CommodityId,
                                    SKUName = a.SKUName,
                                    Quantity = a.Quantity,
                                    Price = a.Price,
                                    SKUImageUrl = a.SKUImageUrl,
                                    SortNum = a.SortNum
                                }).ToList();

                _cache.SetToDistributedCache(cacheKey, sco);
                return ExecResult<CommodityDetailSco>.RetSuccess(sco, "获取商品详情成功");
            }
            catch (Exception ex)
            {
                logger.LogError(ex, ex.Message);
            }
            finally
            {
                RedisClientService.TryReleaseDistributedLock(cacheKey);
            }
            return ExecResult<CommodityDetailSco>.Error("获取商品详情失败");
        }

        /// <summary>
        /// 获取商品详情（逻辑过期应对缓存击穿，需要提前缓存预热）
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ExecResult<CommodityDetailSco>> GetCommodityDetailLogicExpire(string id)
        {
            string cacheKey = $"{COMMODITY_DETAIL_CACHE_TAG}:{id}";
            bool cacheResult = _cache.TryGetFromDistributedCacheWithExpire(cacheKey, out bool isExpired, out CommodityDetailSco scoInCache);

            if (cacheResult && !isExpired)
            {
                if (scoInCache != null)
                {
                    return await Task.FromResult(ExecResult<CommodityDetailSco>.RetSuccess(scoInCache, "获取商品详情成功"));
                }
                else
                {
                    return ExecResult<CommodityDetailSco>.Error("未找到该商品");
                }
            }

            bool isGetLock = RedisClientService.TryGetDistributedLock(cacheKey);
            try
            {
                // 获取到锁的线程开启异步任务更新缓存
                if (isGetLock)
                {
                    // DoubleCheck 缓存有效期
                    bool cacheResult1 = _cache.TryGetFromDistributedCacheWithExpire(cacheKey, out bool isExpired1, out CommodityDetailSco scoInCache1);
                    if (!cacheResult1 || isExpired1)
                    {
                        ThreadPool.QueueUserWorkItem(async (state) =>
                        {
                            Thread.Sleep(500);

                            try
                            {
                                using (var scope = GlobalReadOnlyVariables.ServiceProvider.CreateScope())
                                {
                                    var repoCommodity1 = scope.ServiceProvider.GetService<BaseRepoMysql<Commodity>>();
                                    var repoCommoditySKU1 = scope.ServiceProvider.GetService<BaseRepoMysql<CommoditySKU>>();
                                    var repoCommodityImage1 = scope.ServiceProvider.GetService<BaseRepoMysql<CommoditySlideShowImage>>();

                                    Console.WriteLine($"查询了数据库 - {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")}");
                                    CommodityDetailSco sco = new CommodityDetailSco();
                                    Commodity commodityEntity = await repoCommodity1.FindAsyncNoTracking(a => a.Id == id);
                                    if (commodityEntity == null)
                                    {
                                        // 向缓存中设置空对象
                                        _cache.SetToDistributedCacheWithExpire(cacheKey, MCacheExt.NULL_CACHE_VALUE, MCacheExt.NULL_CACHE_TTL_MINUTES);
                                        return;
                                    }

                                    List<CommoditySlideShowImage> commodityImageEntities = await repoCommodityImage1.ListAsyncNoTracking(a => a.CommodityId == id);
                                    List<CommoditySKU> commoditySKUEntities = await repoCommoditySKU1.ListAsyncNoTracking(a => a.CommodityId == id);

                                    sco.Main = new Commodity()
                                    {
                                        Id = commodityEntity.Id,
                                        CommodityName = commodityEntity.CommodityName,
                                        Intro = commodityEntity.Intro,
                                        MainImageUrl = commodityEntity.MainImageUrl,
                                        Manufacture = commodityEntity.Manufacture
                                    };

                                    sco.SlideShowImageList = commodityImageEntities.OrderBy(a => a.SortNum)
                                                        .Select(a => new CommoditySlideShowImage
                                                        {
                                                            Id = a.Id,
                                                            ImageUrl = a.ImageUrl,
                                                            SortNum = a.SortNum
                                                        }).ToList();

                                    sco.SKUList = commoditySKUEntities.OrderBy(a => a.SortNum)
                                                    .Select(a => new CommoditySKU
                                                    {
                                                        Id = a.Id,
                                                        CommodityId = a.CommodityId,
                                                        SKUName = a.SKUName,
                                                        Quantity = a.Quantity,
                                                        Price = a.Price,
                                                        SKUImageUrl = a.SKUImageUrl,
                                                        SortNum = a.SortNum
                                                    }).ToList();

                                    _cache.SetToDistributedCacheWithExpire(cacheKey, sco);
                                }
                            }
                            catch (Exception ex)
                            {
                                logger.LogError(ex, ex.Message);
                            }
                            finally
                            {
                                RedisClientService.TryReleaseDistributedLock(cacheKey);
                            }
                        });
                    }
                }

                // 所有线程返回过期数据即可
                return await Task.FromResult(ExecResult<CommodityDetailSco>.RetSuccess(scoInCache, "获取商品详情成功"));
            }
            catch (Exception ex)
            {
                logger.LogError(ex, ex.Message);
            }

            return ExecResult<CommodityDetailSco>.Error("获取商品详情失败");
        }

        /// <summary>
        /// 编辑 商品
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="req"></param>
        /// <param name="mainImageUrl"></param>
        /// <returns></returns>
        public async Task<ExecResult> EditCommodityMain(string currentUserId, EditCommodityDto dto)
        {
            var duplicateNameQuery = repoCommodity.QueryNoTracking(a => a.Id != dto.Id && a.CommodityName == dto.CommodityName);
            if (duplicateNameQuery.Any())
            {
                return new ExecResult { Success = false, ErrorMsg = "已经存在相同名称的商品！" };
            }

            Commodity entity = repoCommodity.Find(a => a.Id == dto.Id);

            entity.CategoryId = dto.CategoryId;
            entity.CommodityName = dto.CommodityName;
            entity.Manufacture = dto.Manufacture;
            entity.MainImageUrl = dto.MainImageUrl;

            entity.Modifier = currentUserId;
            entity.ModifyTime = DateTime.Now;

            repoCommodity.Update(entity);

            await repoCommodity.SaveChangesAsync();
            RemoveCommodityDetailCache(entity.Id);
            return new ExecResult { Success = true, ErrorMsg = "编辑商品信息成功！" };
        }

        /// <summary>
        /// 删除商品
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ExecResult> DeleteCommodity(string currentUserId, string id)
        {
            var entity = await repoCommodity.FindAsync(a => a.Id == id);
            if (entity == null)
            {
                return ExecResult.Error("要删除的商品不存在！");
            }

            repoCommodity.Delete(currentUserId, entity);
            await repoCommodity.SaveChangesAsync();
            RemoveCommodityDetailCache(entity.Id);
            return ExecResult.ExecSuccessMsg("删除成功");
        }

        /// <summary>
        /// 修改商品的图片
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="commodityId"></param>
        /// <param name="dtoItems"></param>
        /// <returns></returns>
        public async Task<ExecResult> EditCommoditySlideShowImages(string currentUserId, string commodityId,
            List<CommoditySlideShowImage> dtoItems)
        {
            var imageExistEntities = await repoCommodityImage.ListAsync(a => a.CommodityId == commodityId);

            IEnumerable<string> reqItemIds = dtoItems.Where(a => !string.IsNullOrEmpty(a.Id)).Select(a => a.Id);
            bool isNewAdd = !reqItemIds.Any();  // 如果都没有 Id，则说明全部都是新增

            // 删除
            var toDeleteEntities = imageExistEntities.Where(a => !reqItemIds.Contains(a.Id));
            foreach (var toDeleteItem in toDeleteEntities)
            {
                repoCommodityImage.Delete(currentUserId, toDeleteItem);
            }

            // 编辑（图片更换 或 顺序变了）
            foreach (var entity in imageExistEntities)
            {
                var dtoExistItem = dtoItems.Where(a => a.Id == entity.Id).FirstOrDefault();

                // 传入的项不存在与数据库对应的情况
                if (dtoExistItem == null)
                {
                    continue;
                }

                // 如果字段值与数据库不同则需要编辑
                if (dtoExistItem.ImageUrl != entity.ImageUrl
                    || dtoExistItem.SortNum != entity.SortNum)
                {
                    entity.ImageUrl = dtoExistItem.ImageUrl;
                    entity.SortNum = entity.SortNum;
                }

                entity.Modifier = currentUserId;
                entity.ModifyTime = DateTime.Now;

                repoCommodityImage.Update(entity);
            }

            // 添加
            var toAddItems = dtoItems.Where(a => string.IsNullOrEmpty(a.Id)).ToList();
            foreach (var toAddItem in toAddItems)
            {
                CommoditySlideShowImage commodityImage = new CommoditySlideShowImage();
                commodityImage.Id = GuidFactory.NewSortedGuid();
                commodityImage.CommodityId = commodityId;
                commodityImage.ImageUrl = toAddItem.ImageUrl;

                commodityImage.Creator = currentUserId;
                commodityImage.CreateTime = DateTime.Now;

                repoCommodityImage.Add(commodityImage);
            }

            await dbContext.SaveChangesAsync();
            RemoveCommodityDetailCache(commodityId);

            return new ExecResult
            {
                Success = true,
                ErrorMsg = (isNewAdd ? "新增" : "编辑") + "成功"
            };
        }

        /// <summary>
        /// 新增/编辑商品的SKU
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ExecResult> EditCommoditySKU(string currentUserId, EditCommoditySKUDto dto)
        {
            bool isCreate = string.IsNullOrEmpty(dto.Id);

            var duplicateNameQuery = repoCommoditySKU.QueryNoTracking(a => a.SKUName == dto.SKUName && a.CommodityId == dto.CommodityId);

            // 新增
            if (isCreate)
            {
                if (duplicateNameQuery.Any())
                {
                    return new ExecResult { Success = false, ErrorMsg = "该商品已经存在相同名称的 SKU！" };
                }

                CommoditySKU sku = new CommoditySKU();
                sku.Id = GuidFactory.NewSortedGuid();
                sku.CommodityId = dto.CommodityId;
                sku.SKUName = dto.SKUName;
                sku.Quantity = dto.Quantity;
                sku.Price = dto.Price;
                sku.SKUImageUrl = dto.SKUImageUrl;
                sku.SortNum = dto.SortNum;

                sku.Creator = currentUserId;
                sku.CreateTime = DateTime.Now;

                repoCommoditySKU.Add(sku);
            }
            // 编辑
            else
            {
                duplicateNameQuery = duplicateNameQuery.Where(a => a.Id != dto.Id);
                if (duplicateNameQuery.Any())
                {
                    return new ExecResult { Success = false, ErrorMsg = "该商品已经存在相同名称的 SKU！" };
                }

                CommoditySKU entity = repoCommoditySKU.Find(a => a.Id == dto.Id);
                //entity.CommodityId = dto.CommodityId; // 修改不应修改其属于的商品
                entity.SKUName = dto.SKUName;
                entity.Quantity = dto.Quantity;
                entity.Price = dto.Price;
                entity.SKUImageUrl = dto.SKUImageUrl;
                entity.SortNum = dto.SortNum;

                entity.Modifier = currentUserId;
                entity.ModifyTime = DateTime.Now;

                repoCommoditySKU.Update(entity);
            }

            await dbContext.SaveChangesAsync();
            RemoveCommodityDetailCache(dto.CommodityId);
            return new ExecResult { Success = true, ErrorMsg = $"{(isCreate ? "创建" : "编辑")} SKU 成功！" };
        }

        /// <summary>
        /// 删除商品 SKU
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ExecResult> DeleteCommoditySKU(string currentUserId, string id)
        {
            var entity = await repoCommoditySKU.FindAsync(a => a.Id == id);
            if (entity == null)
            {
                return ExecResult.Error("要删除的商品 SKU 不存在！");
            }

            repoCommoditySKU.Delete(currentUserId, entity);
            repoCommoditySKU.SaveChanges();
            RemoveCommodityDetailCache(entity.Id);

            return ExecResult.ExecSuccess;
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="id"></param>
        private void RemoveCommodityDetailCache(string id)
        {
            string cacheKey = $"{COMMODITY_DETAIL_CACHE_TAG}:{id}";
            _cache.Remove(cacheKey);

            // TODO 需要实现延迟再次删除策略
        }


        #region 商品分类

        /// <summary>
        /// 获取全部商品分类列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<List<CommodityCategory>> GetAllCommodityCategoryList(ListCommodityCategoryDto dto)
        {
            var query = repoCommodityCategory.QueryNoTracking(a => true);
            if (!string.IsNullOrEmpty(dto.CategoryName))
            {
                query = query.Where(a => a.CategoryName.Contains(dto.CategoryName));
            }

            query = query.OrderBy(a => a.SortNum);

            return await query.ToListAsync();
        }

        /// <summary>
        /// 获取商品分类列表
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<QueryResult<CommodityCategory>> GetCommodityCategoryList(ListCommodityCategoryDto dto)
        {
            var query = repoCommodityCategory.QueryNoTracking(a => true);
            if (!string.IsNullOrEmpty(dto.CategoryName))
            {
                query = query.Where(a => a.CategoryName.Contains(dto.CategoryName));
            }

            if (!string.IsNullOrEmpty(dto.SortField))
            {
                var ordering = CommonUtils.MakeOrderExpression<CommodityCategory>(dto.SortField);
                return await repoMysql.QueryResultByPageAsync(query, ordering, dto, a => a.Id, true);
            }
            else
            {
                query = query.OrderByDescending(a => a.CreateTime).ThenBy(a => a.Id);
                return await repoMysql.QueryResultByPageAsync(query, dto);
            }            
        }

        /// <summary>
        /// 编辑商品分类
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task<ExecResult> EditCommodityCategory(string currentUserId, EditCommodityCategoryDto dto)
        {
            bool isCreate = string.IsNullOrEmpty(dto.Id);

            var duplicatedCategoryNameQuery = repoCommodityCategory.Query(a => a.CategoryName == dto.CategoryName);

            if (isCreate)
            {
                if (duplicatedCategoryNameQuery.Any())
                {
                    return ExecResult.Error($"已经存在名称为：{dto.CategoryName} 的商品分类");
                }

                CommodityCategory commodityCategory = new CommodityCategory();
                commodityCategory.Id = GuidFactory.NewSortedGuid();
                commodityCategory.CategoryName = dto.CategoryName;
                commodityCategory.SortNum = dto.SortNum;

                commodityCategory.Creator = currentUserId;
                commodityCategory.CreateTime = DateTime.Now;

                repoCommodityCategory.Add(commodityCategory);
            }
            else
            {
                duplicatedCategoryNameQuery = duplicatedCategoryNameQuery.Where(a => a.Id != dto.Id);
                if (duplicatedCategoryNameQuery.Any())
                {
                    return ExecResult.Error($"已经存在名称为：{dto.CategoryName} 的商品分类");
                }

                CommodityCategory entity = await repoCommodityCategory.FindAsync(a => a.Id == dto.Id);
                if (entity == null)
                {
                    return ExecResult.Error($"要编辑的商品分类不存在");
                }

                entity.CategoryName = dto.CategoryName;
                entity.SortNum = dto.SortNum;
                entity.Modifier = currentUserId;
                entity.ModifyTime = DateTime.Now;

                repoCommodityCategory.Update(entity);
            }

            await dbContext.SaveChangesAsync();
            return ExecResult.ExecSuccessMsg($"{(isCreate ? "创建" : "编辑")} 商品分类 成功！");
        }

        /// <summary>
        /// 删除商品分类
        /// </summary>
        /// <param name="currentUserId"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<ExecResult> DeleteCommodityCategory(string currentUserId, string id)
        {
            var entity = await repoCommodityCategory.FindAsync(a => a.Id == id);
            if (entity == null)
            {
                return ExecResult.Error("要删除的商品分类不存在");
            }

            var alreadyUseQuery = repoCommodity.QueryNoTracking(a => a.CategoryId == id);
            if (alreadyUseQuery.Any())
            {
                return ExecResult.Error("分类已经被使用");
            }

            repoCommodityCategory.Delete(currentUserId, entity);
            await dbContext.SaveChangesAsync();

            return ExecResult.ExecSuccessMsg("删除成功");
        }

        #endregion
    }
}
